package com.everbest.admin.serviceImpl;


import com.everbest.admin.service.RedisService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.DateUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.util.concurrent.TimeUnit;


/**
 * redis 分布式锁处理并发
 */

@Service
@Slf4j
public class RedisServiceImpl implements RedisService {

    @Autowired
    private RedisTemplate redisTemplate;
    /**
     * 最大重试次数
     */
    private final Integer MAX_RETRY_COUNT = 16;

    @Override
    public boolean lockTaskExpiresByRedis(String key, Long expires) {
        //获取锁
        if (redisTemplate.opsForValue().setIfAbsent(key, String.valueOf(expires))) {
            redisTemplate.expire(key, DateUtils.MILLIS_PER_HOUR, TimeUnit.HOURS);
            return true;
        }
        //没有获取锁
        String value = String.valueOf(redisTemplate.opsForValue().get(key));//获取value值
        //如果锁过期了
        if (!StringUtils.isEmpty(value) && Long.parseLong(value) < System.currentTimeMillis()) {
            //锁超时后,调用getAndSet()方法，返回旧值，设置新值
            String oldValue = String.valueOf(redisTemplate.opsForValue().getAndSet(key, String.valueOf(expires)));
            //如果旧值等于当前的值，则证明同意线程操作，获取锁成功，返回true
            if (!StringUtils.isEmpty(oldValue) && value.equals(oldValue)) {
                redisTemplate.expire(key, DateUtils.MILLIS_PER_HOUR, TimeUnit.HOURS);
                return true;
            }
        }
        return false;
    }

    /**
     * 加锁
     *
     * @param key
     * @param expires
     * @return
     */
    @Override
    public boolean lockTaskByRedis(String key, Long expires) {
        //获取锁 :   这个其实就是setNX命令，返回的是boolean
        // 这里将值设置为过期时间 ,事实上 该key 永不过期
        if (redisTemplate.opsForValue().setIfAbsent(key, String.valueOf(expires))) {
            return true;
        }
        //没有获取锁
        String value = String.valueOf(redisTemplate.opsForValue().get(key));//获取value值
        //如果锁过期了 (当前时间 大于 key 对应的值)
        if (!StringUtils.isEmpty(value) && Long.parseLong(value) < System.currentTimeMillis()) {

            //锁超时后,调用getAndSet()方法，返回旧值(上一个锁的时间)，设置新值
            String oldValue = String.valueOf(redisTemplate.opsForValue().getAndSet(key, String.valueOf(expires)));

            /* 如果旧值等于当前的值，则证明同意线程操作，获取锁成功，返回true
             * 只会让一个线程拿到锁
             * 如果旧的value和currentValue相等，只会有一个线程达成条件，因为第二个线程拿到的oldValue已经和currentValue不一样了
             */
            if (!StringUtils.isEmpty(oldValue) && value.equals(oldValue)) {
                return true;
            }
        }
        return false;
    }

    /**
     * 释放锁
     *
     * @param key
     * @param expires
     */
    @Override
    public void unlockTaskByRedis(String key, Long expires) {
        String currentVaule = String.valueOf(redisTemplate.opsForValue().get(key));
        try {
            if (!StringUtils.isEmpty(currentVaule) && currentVaule.equals(String.valueOf(expires))) {
                redisTemplate.opsForValue().getOperations().delete(key);//删除缓存
            }
        } catch (Exception ex) {
            log.info(ex.getMessage());
        }
    }
}
